package me.yuxiaoyao.lock.annotation;

import me.yuxiaoyao.lock.interceptor.AbstractFallbackDistributedLockOperationSource;
import me.yuxiaoyao.lock.interceptor.DistributedLockOperation;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

/**
 * @author kerryzhang on 2020/12/22
 */


public class AnnotationDistributedLockOperationSource extends AbstractFallbackDistributedLockOperationSource {
    private final boolean publicMethodsOnly;
    private final Set<DistributedLockAnnotationParser> parsers;

    public AnnotationDistributedLockOperationSource() {
        this(true, Collections.singleton(new DefaultDistributedLockAnnotationParser()));
    }

    public AnnotationDistributedLockOperationSource(boolean publicMethodsOnly) {
        this(publicMethodsOnly, Collections.singleton(new DefaultDistributedLockAnnotationParser()));
    }

    public AnnotationDistributedLockOperationSource(boolean publicMethodsOnly, Set<DistributedLockAnnotationParser> parsers) {
        this.publicMethodsOnly = publicMethodsOnly;
        this.parsers = parsers;
    }

    @Override
    public boolean isCandidateClass(Class<?> targetClass) {
        for (DistributedLockAnnotationParser parser : this.parsers) {
            if (parser.isCandidateClass(targetClass)) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected boolean allowPublicMethodsOnly() {
        return this.publicMethodsOnly;
    }

    @Override
    protected Collection<DistributedLockOperation> findDistributedLockOperations(Method method) {
        return determineLockOperations(parser -> parser.parseDistributedLockAnnotations(method));
    }

    @Override
    protected Collection<DistributedLockOperation> findDistributedLockOperations(Class<?> clazz) {
        return determineLockOperations(parser -> parser.parseDistributedLockAnnotations(clazz));
    }


    @Nullable
    protected Collection<DistributedLockOperation> determineLockOperations(Provider provider) {
        Collection<DistributedLockOperation> ops = null;
        // 可能存在多个Parser
        for (DistributedLockAnnotationParser parser : this.parsers) {
            // first
            Collection<DistributedLockOperation> annOps = provider.getLockOperations(parser);
            if (annOps != null) {
                if (ops == null) {
                    ops = annOps;
                } else {
                    Collection<DistributedLockOperation> combined = new ArrayList<>(ops.size() + annOps.size());
                    combined.addAll(ops);
                    combined.addAll(annOps);
                    ops = combined;
                }
            }
        }
        return ops;
    }


    @FunctionalInterface
    private interface Provider {
        /**
         * get
         *
         * @param parser
         * @return
         */
        @Nullable
        Collection<DistributedLockOperation> getLockOperations(DistributedLockAnnotationParser parser);
    }

}
